﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Threading;
using EzDesk.Attendance.AttendanceImpl.Repositories;
using EzDesk.Attendance.Model;
using EzDesk.Attendance.AttendanceImpl.Filters;
using EzDesk.EzAppApi.Utility;


namespace EzDesk.Attendance.AttendanceImpl
{
    public class AttendanceService
    {
        #region [ 构造器 ]
        private AttendanceRepository _repository;
        private AttendanceEvaluatorFactory _attendanceEvaluatorFactory;
        private IEzApiClient _coreService;

        public AttendanceService(AttendanceRepository repository, IEzApiClient coreService)
        {
            this._coreService = coreService;
            this._repository = repository;
            this._attendanceEvaluatorFactory = new AttendanceEvaluatorFactory(this, _repository);
        }

        // todo: 使用DI加载
        public AttendanceService()
            : this(new AttendanceRepository(), new EzApiClient())
        {
        }

        #endregion


        #region [ 系统基础信息服务 ]
         ///<summary>
         ///获得考勤系统基本配置信息
         ///</summary>
         ///<returns></returns>
        public AttendanceConfig GetAttendanceConfig()
        {
            return AttendanceConfig.Cfg;
        }
        #endregion

        /// <summary>
        /// 获得某个用户一天的所有考勤记录
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="day"></param>
        /// <returns></returns>
        public IList<AttendanceInfo> GetAttendanceInfoByUser(Guid userId, DateTime day)
        {
            return _repository.GetAttendanceInfos()
                .ByUserId(userId)
                .InDay(day)
                .OrderBy(p => p.ActionTime)
                .ToList();
        }

        /// <summary>
        /// 获得考勤状况
        /// </summary>
        /// <param name="dutyType">考勤类型</param>
        /// <param name="day">考勤日期</param>
        /// <param name="userIdsStr">用户Id</param>
        /// <returns></returns>
        public IList<AttendanceInfo> GetAttendanceInfo(DutyTypeEnum dutyType, DateTime day, string userIdsStr)
        {
            IList<AttendanceInfo> returnList = new List<AttendanceInfo>();

            var userInfos = _coreService.GetSimpleUserInfos(userIdsStr);
            foreach (var userInfo in userInfos)
            {
                var attendance = _repository.GetAttendanceInfos()
                    .ByUserId(userInfo.Id)
                    .ByActionType(dutyType)
                    .SingleOrDefault();
                if (attendance != null)
                {
                    returnList.Add(attendance);
                }
            }
            return returnList;
        }

        #region [ 考勤状况和考勤评价 ]

        /// <summary>
        /// 获得某一天天的工作要求
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public DayInfo GetDayInfo(DateTime date)
        {
            var legalHolday = _repository.GetLegalHolidayInfo(date);
            var extraShift = _repository.GetExtraShiftInfo(date);

            return new DayInfo(date)
            {
                HolidayInfo = legalHolday,
                ExtraShiftInfo = extraShift,
            };
        }


        /// <summary>
        /// 获得某个用户某天考勤信息的评价
        /// </summary>
        /// <param name="userIdStr"></param>
        /// <param name="day"></param>
        /// <returns></returns>
        public AttendanceEvaluationInfo GetEvaluatedAttendanceInfo(Guid userId, DateTime day)
        {
            AttendanceEvaluationInfo evaluationInfo =
                _attendanceEvaluatorFactory
                .CreateEvaluator(userId, day)
                .Evaluate();
            return evaluationInfo;
        }

        /// <summary>
        /// 进行打考勤
        /// </summary>
        /// <param name="userIdStr"></param>
        /// <param name="dutyType"></param>
        /// <param name="actionTime"></param>
        /// <param name="explaination"></param>
        public void CheckAttendance(
            Guid userId, DutyTypeEnum dutyType,
            DateTime actionTime, string explaination)
        {
            // 先确定这次打卡考勤是合法的


            //查询是否已经存在条目，没有则创建，已经存在则返回Id
            // ？？？
            AttendanceInfo attendance = _repository.GetAttendanceInfos()
                .ByUserId(userId)
                .ByActionType(dutyType)
                .InDay(DateTime.Today)
                .SingleOrDefault();

            if (attendance == null)
            {
                //todo:....
                attendance = new AttendanceInfo()
                {
                    Id = Guid.NewGuid(),
                    Day = DateTime.Today,
                    DutyType = dutyType,
                    ActionTime = actionTime,
                    UserId = userId,
                };
                _repository.CreateAttendanceInfo(attendance);
            }

            //todo: 考勤错误处理等


            //已有条目，直接签到
            attendance.ActionTime = actionTime;
            attendance.Explanation = explaination;

            _repository.UpdateAttendanceInfo(attendance);
        }

        /// <summary>
        /// 申请自定义某天的调休时间
        /// </summary>
        /// <param name="userIdStr"></param>
        /// <param name="day"></param>
        /// <param name="desireShiftWorkMiniutes"></param>
        /// <param name="explaination"></param>
        public void ApplyForCustmizedShiftWork(
            Guid userId, DateTime day,
            int desireShiftWorkMiniutes, string explaination
            )
        {
            var existedCustomizedShiftWork = _repository.GetCustomizedShiftWorks()
                .ByUserIdAndDay(userId, day)
                .SingleOrDefault();
            if (existedCustomizedShiftWork == null)
            {
                _repository.CreateCustomizedShiftWork(
                    new CustomizedShiftWork()
                    {
                        UserId = userId,
                        Day = day,
                        AuditedUserId = null,
                        CustomizedShitWorkMiniute = desireShiftWorkMiniutes,
                    }
                );
            }
            else
            {
                existedCustomizedShiftWork.AuditedUserId = null;
                existedCustomizedShiftWork.CustomizedShitWorkMiniute = desireShiftWorkMiniutes;
                _repository.UpdateCustomizedShiftWork(existedCustomizedShiftWork);
            }

            //todo: 发送Notificaiton
            Debug.WriteLine("通知考勤管理员。 确认更改时间");
        }

        #endregion

        #region [ 假期相关 ]


        /// <summary>
        /// 申请新的假期
        /// </summary>
        /// <param name="userIdStr"></param>
        /// <param name="leaveTypeIdStr"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <param name="explaination"></param>
        /// <returns></returns>
        public string ApplyForNewLeave(
            Guid userId /* 用户 Id */,
            Guid leaveTypeId /* 假期类型Id */,
            DateTime startTime /* 请假开始时间 */,
            DateTime endTime /* 请假结束时间 */,
            string explaination /* 备注解释 */)
        {
            var leaveType = _repository.GetLeaveTypes()
                .ByLeaveTypeId(leaveTypeId);

            var user = _coreService.GetSimpleUserById(userId);

            Guid newLeaveId = Guid.NewGuid();
            var leave =
               new LeaveInfo()
               {
                   LeaveId = newLeaveId,
                   StartTime = startTime,
                   EndTime = endTime,
                   IsAudited = false,
                   LeaveTypeId = leaveType.LeaveTypeId,
                   LeaveTypeName = leaveType.LeaveTypeName,
                   IsForWork = leaveType.IsForWork,
                   Description = explaination,
                   UserId = user.Id,
                   UserName = user.UserName,
               };


            ValidateLeaveInfo(leave);

            _repository.CreateLeaveInfo(leave);

            return newLeaveId.ToString();
        }


        public void UpdateLeaveInfo(LeaveInfo thisLeave)
        {
            // 已经审核，不能更改
            if (thisLeave.IsAudited)
            {
                throw new Exception("已经审核，不能更改");
            }
            var leave = GetLeaveInfo(thisLeave.LeaveId);

            // 不能更改审核信息
            if (leave.IsAudited != thisLeave.IsAudited)
            {
                throw new Exception("不能更改审核信息");
            }

            ValidateLeaveInfo(thisLeave);

            _repository.UpdateLeaveInfo(thisLeave);
        }

        private void ValidateLeaveInfo(LeaveInfo leave)
        {
            //todo: 判断申请合法性
            if (leave.EndTime <= leave.StartTime)
            {
                throw new Exception("请假时间不合法");
            }

            //判断目标时间段是否存在假期
            var errorLeaves = _repository.GetAllLeaveInfo().ByUserId(leave.UserId)
                .Where(p => leave.StartTime < p.EndTime && leave.EndTime > p.StartTime && p.LeaveId != leave.LeaveId);
            if (errorLeaves.Count() > 0)
            {
                //todo: 假期申请错误
                throw new Exception("请假时间和已有假期重复");
            }
        }



        /// <summary>
        /// 获得所有假期类型
        /// </summary>
        /// <returns></returns>
        public IList<LeaveType> GetAllLeaveTypes()
        {
            return _repository.GetLeaveTypes()
                .ToList();
        }

        /// <summary>
        /// 获得某个用户某段时间内的假期信息
        /// </summary>
        /// <param name="userIdStr">用户Id</param>
        /// <param name="startDay">开始时间</param>
        /// <param name="endDay">结束时间</param>
        /// <returns></returns>
        public IList<LeaveInfo> GetLeaveInfo(Guid userId, DateTime? startDay, DateTime? endDay)
        {
            var leaves = _repository.GetAllLeaveInfo()
                .ByUserId(userId)
                .BetweenTwoDays(startDay, endDay)
                .ToList();

            return leaves;
        }

        /// <summary>
        /// 获得一条假期的详细信息
        /// </summary>
        /// <param name="leaveId"></param>
        /// <returns></returns>
        public LeaveInfo GetLeaveInfo(Guid leaveId)
        {
            return _repository.GetAllLeaveInfo().ByLeaveId(leaveId).SingleOrDefault();
        }

        /// <summary>
        /// 获得某一天的假期信息
        /// </summary>
        /// <param name="userIdStr"></param>
        /// <param name="day"></param>
        public DayLeaveInfo GetDayLeaveInfo(Guid userId, DateTime day)
        {
            var leaves = _repository.GetAllLeaveInfo()
                .ByUserId(userId)
                .BetweenTwoDays(day, day);

            var dayLeaves = new DayLeaveInfo(day);
            dayLeaves = generateDayLeaveItemInfo(leaves.ToList(), day);
            return dayLeaves;
        }

        private DayLeaveInfo generateDayLeaveItemInfo(IList<LeaveInfo> leaveInfos, DateTime day)
        {
            var dayLeaveInfo = new DayLeaveInfo(day);

            List<DayLeaveItemInfo> leaveItems = new List<DayLeaveItemInfo>();
            DateTime stdDayStartWorkTime = GetAttendanceConfig().StdOnDutyTime.ToDateTime(day);
            DateTime stdDayEndWorkTime = GetAttendanceConfig().StdOffDutyTime.ToDateTime(day);

            leaveInfos.ToList().ForEach(
                leave =>
                {
                    DayLeaveItemInfo dayLeave = new DayLeaveItemInfo()
                    {
                        LeaveId = leave.LeaveId,
                        LeaveName = leave.LeaveTypeName,
                        IsForWork = leave.IsForWork,
                        Day = day,
                    };
                    dayLeave.StartTime = leave.StartTime < stdDayStartWorkTime ? stdDayStartWorkTime : leave.StartTime;
                    dayLeave.EndTime = leave.EndTime > stdDayEndWorkTime ? stdDayEndWorkTime : leave.EndTime;

                    leaveItems.Add(dayLeave);

                    leaveItems = leaveItems.OrderBy(l => l.StartTime).ToList();

                    dayLeaveInfo.IsWholeDay = dayLeaveInfo.IsWholeDay | leave.StartTime <= stdDayStartWorkTime && leave.EndTime >= stdDayEndWorkTime;
                }
                );
            dayLeaveInfo.Leaves = leaveItems;
            return dayLeaveInfo;
        }

        /// <summary>
        /// 对一条假期进行审核
        /// </summary>
        /// <param name="leaveId"></param>
        /// <param name="auditUser"></param>
        /// <param name="auditNote"></param>
        public void AuditLeaveInfo(Guid leaveId, string auditUserIdStr, string auditNote)
        {
            var auditUser = _coreService.GetSimpleUserInfos(auditUserIdStr).FirstOrDefault();

            var leave = _repository.GetAllLeaveInfo().ByLeaveId(leaveId).SingleOrDefault();
            leave.IsAudited = true;
            leave.AuditUserId = auditUser.Id;
            leave.AuditUserName = auditUser.Name;
            leave.AuditTime = DateTime.Now;
            leave.AuditNote = auditNote;

            _repository.UpdateLeaveInfo(leave);
        }

        /// <summary>
        /// 删除一条请假
        /// </summary>
        /// <param name="guid"></param>
        public void RemoveLeaveInfo(Guid leaveId, Guid userId)
        {
            var leave = _repository.GetAllLeaveInfo().ByLeaveId(leaveId).FirstOrDefault();
            if (leave == null)
            {
                throw new Exception("找不到要删除的信息");
            }

            var cfg = GetAttendanceConfig();

            if (leave.IsAudited)
            {
                //已审核的只能由管理员删除
                if (!cfg.AttendanceManagerUserIds.Contains(userId.ToString()))
                {
                    throw new Exception("只能由管理员删除已经审核的信息");
                }
            }
            else
            {
                // 未审核的请假只能由自己或者管理员删除
                if (!cfg.AttendanceManagerUserIds.Contains(userId.ToString()) && leave.UserId != userId)
                {
                    throw new Exception("未审核的请假只能由自己或者管理员删除");
                }
            }


            _repository.RemoveLeaveInfo(leaveId);
        }

        #endregion



    }
}